package jmind.core.aspect;

import jmind.base.annotation.MethodMonitor;
import jmind.base.annotation.MethodMonitor.Store;
import jmind.base.util.AppEnvironment;
import jmind.base.util.DataUtil;
import jmind.base.util.IpUtil;
import jmind.core.cache.support.StatsCounterCache;
import jmind.core.manager.MongoManager;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.perf4j.StopWatch;
import org.perf4j.slf4j.Slf4JStopWatch;
import org.springframework.core.annotation.Order;

import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 使用方法，在方法前 @MethodMonitor(store = Store.file)
 * 方法在同一个类里面不能被aop ，例如 在类Test 里面切面a 方法，如果b方法调用a 方法，执行b 方法不能切面a方法
 * @author weibo-xie
 * 2012-6-8
 */
@Aspect
@Order(Integer.MAX_VALUE)
public class MethodMonitorAspect {

    public  static final StatsCounterCache STATS_COUNTER_CACHE = new StatsCounterCache(1000);

    public int slowTime= DataUtil.toInt(AppEnvironment.getProperty("methodMonitor.slow","200"));
    /**
     * execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)

    这是一个正则表达式，其中由 '?' 结尾的部分是可选的。翻译过来就是：

    执行(方法访问修饰符? 方法返回类型 声明类型? 方法名(方法参数类型) 抛出异常?)

    所有的这些都是用来定义执行切入点，即那些方法应该被侯选为切入点：

    方法访问修饰符   即 public, private 等等
    方法返回类型       即方法返回的类型，如 void, String 等等
    声明类型                1.5的语法，现在可以先忽略它
    方法名                    方法的名字
    方法参数类型       方法的参数类型
    抛出异常                方法声明的抛出的异常

     *  任意共用方法执行   execution(public * *(..))
     *  任何一个以set 开始的方法execution( * set*(..))
     *  Userservice接口的任意方法执行：execution(*  com.service.UserService.*(..))
     *  定义在service包里的任意方法执行： execution(*  com.service.UserService.*.*(..))
     *  定义在 service包或者子包里的任意方法执行： execution(*  com.service..*.*(..))
     *
     *            execution(public * com.zhimei.platform.web.scheduler.*.set*(..))
     * @Pointcut("execution(public * com.zhimei.platform.web.controller.*.*(..))")
     * @Pointcut("@annotation(com.zhimei.platform.web.aspect.AccessControl)")
       @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
     */
//    @Pointcut("@annotation(jmind.base.annotation.MethodMonitor)")
//    public void exe2() {
//    }

    @Around(value = "execution(* *(..)) && @annotation(m)", argNames = "pjp,m")
    public Object doAround(ProceedingJoinPoint pjp,MethodMonitor m) throws Throwable {

       MethodSignature signature = (MethodSignature) pjp.getSignature();
//        final MethodMonitor m = JoinPoints.getAnnotation(pjp, MethodMonitor.class);
        /****
         * signature.getDeclaringTypeName() 全名，包括包名
         * signature.getName() 方法名
         */
        //	System.out.println(signature.getDeclaringTypeName()+"&"+signature.getName()+"&"+signature.toLongString()+"&"+signature.toShortString()+"&"+signature.getDeclaringType());
        //	System.out.println(signature.getMethod().getName()+"%"+signature.getReturnType());
        //	 	com.zhimei.platform.web.controller.TestController&test2&public java.lang.String com.zhimei.platform.web.controller.TestController.test2(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)&TestController.test2(..)&class com.zhimei.platform.web.controller.TestController
        //		test2%class java.lang.String

        Object obj ;

        if (m.store() == Store.mongodb) {
            long start = System.currentTimeMillis();
             obj = pjp.proceed();
            Map<String, Object> arr = new LinkedHashMap<>();
            arr.put("cla", signature.getDeclaringTypeName());
            arr.put("m", signature.getName());
            // 调用服务器ip
            arr.put("ip", IpUtil.getLocalIP());
            if (m.args())
                arr.put("arg", Arrays.toString(pjp.getArgs()));
            if (m.value())
                arr.put("res", obj);
            arr.put("t", (int) (System.currentTimeMillis() - start));
            MongoManager.getInstance().insert("stat", "Monitor", arr);
        } else {
            String name=signature.toShortString();
            StopWatch stopWatch = new Slf4JStopWatch(name);
            try {
                obj = pjp.proceed();
                stopWatch.stop();
                STATS_COUNTER_CACHE.mustGet(name).recordSuccess(stopWatch.getStartTime(),slowTime);
            } catch (Throwable throwable) {
                STATS_COUNTER_CACHE.mustGet(name).recordException(stopWatch.getStartTime());
                throw  throwable;
            }

        }

        return obj;

    }

}
